//
//  CCMatrixs.h
//  ShadowMapping
//
//  Created by Daniel Corleone on 16/6/14.
//  Copyright (c) 2014年 iCloverStudio. All rights reserved.
//

#ifndef ShadowMapping_CCMatrixs_h
#define ShadowMapping_CCMatrixs_h

#include "PlatformConfig.h"
#include "CCDataType.h"
#include "CCVectors.h"


///////////////////////////////////////////////////////////////////////////
// 2x2 matrix
///////////////////////////////////////////////////////////////////////////
template<typename T>
class Matrix2
{
public:
    typedef T value_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    
public:
    union {
        struct {
            value_type m00, m01;
            value_type m10, m11;
        };
        struct {
            value_type M00, M01;
            value_type M10, M11;
        };
        value_type m[4];
    };
public:
    // constructors
    explicit Matrix2();  // init with identity
    explicit Matrix2(const value_type src[4]);
    explicit Matrix2(value_type xx, value_type xy, value_type yx, value_type yy);
    Matrix2(const Matrix2<value_type>& other);
    
public:
    CC_INLINE void        set(const value_type src[4]);
    CC_INLINE void        set(value_type xx, value_type xy, value_type yx, value_type yy);
    CC_INLINE void        setRow(Int32 index, const value_type row[2]);
    CC_INLINE void        setRow(Int32 index, const Vector2<value_type>& v);
    CC_INLINE void        setColumn(Int32 index, const value_type col[2]);
    CC_INLINE void        setColumn(Int32 index, const Vector2<value_type>& v);
    
    CC_INLINE const value_type* get() const;
    CC_INLINE Real       getDeterminant();
    
    CC_INLINE Matrix2<value_type>&    identity();
    CC_INLINE Matrix2<value_type>&    transpose();                            // transpose itself and return reference
    CC_INLINE Matrix2<value_type>&    invert();
    
    // operators
    CC_INLINE Matrix2<value_type>     operator+(const Matrix2<value_type>& rhs) const;    // add rhs
    CC_INLINE Matrix2<value_type>     operator-(const Matrix2<value_type>& rhs) const;    // subtract rhs
    CC_INLINE Matrix2<value_type>&    operator+=(const Matrix2<value_type>& rhs);         // add rhs and update this object
    CC_INLINE Matrix2<value_type>&    operator-=(const Matrix2<value_type>& rhs);         // subtract rhs and update this object
    CC_INLINE Vector2<value_type>     operator*(const Vector2<value_type>& rhs) const;    // multiplication: v' = M * v
    CC_INLINE Matrix2<value_type>     operator*(const Matrix2<value_type>& rhs) const;    // multiplication: M3 = M1 * M2
    CC_INLINE Matrix2<value_type>&    operator*=(const Matrix2<value_type>& rhs);         // multiplication: M1' = M1 * M2
    CC_INLINE Boolean        operator==(const Matrix2<value_type>& rhs) const;   // exact compare, no epsilon
    CC_INLINE Boolean        operator!=(const Matrix2<value_type>& rhs) const;   // exact compare, no epsilon
    CC_INLINE value_type       operator[](Int32 index) const;            // subscript operator v[0], v[1]
    CC_INLINE value_type&      operator[](Int32 index);                  // subscript operator v[0], v[1]
    
    CC_INLINE Matrix2<value_type> operator-() const;                     // unary operator (-)
    CC_INLINE Matrix2<value_type> operator*(Real scalar) const;       // pre-multiplication
    CC_INLINE Matrix2<value_type>& operator*=(Real s);
};

#include "CCMatrix2.cpp"

typedef Matrix2<Int8> Matrix2b;
typedef Matrix2<Int16> Matrix2s;
typedef Matrix2<UInt16> Matrix2us;
typedef Matrix2<Int32> Matrix2i;
typedef Matrix2<UInt32> Matrix2ui;
typedef Matrix2<Int64> Matrix2l;
typedef Matrix2<UInt64> Matrix2ul;
typedef Matrix2<Real> Matrix2f;

///////////////////////////////////////////////////////////////////////////
// 3x3 matrix
///////////////////////////////////////////////////////////////////////////
template<typename T>
class Matrix3
{
public:
    typedef T value_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    
public:
    // constructors
    explicit Matrix3();  // init with identity
    explicit Matrix3(const value_type src[9]);
    explicit Matrix3(value_type xx, value_type xy, value_type xz,
                     value_type yx, value_type yy, value_type yz,
                     value_type zx, value_type zy, value_type zz);
    Matrix3(const Matrix3<value_type>& other);
    
public:
    CC_INLINE void        set(const value_type src[9]);
    CC_INLINE void        set(value_type xx, value_type xy, value_type xz,
                              value_type yx, value_type yy, value_type yz,
                              value_type zx, value_type zy, value_type zz);
    CC_INLINE void        setRow(Int32 index, const value_type row[3]);
    CC_INLINE void        setRow(Int32 index, const Vector3<value_type>& v);
    CC_INLINE void        setColumn(Int32 index, const value_type col[3]);
    CC_INLINE void        setColumn(Int32 index, const Vector3<value_type>& v);
    
    CC_INLINE const value_type* get() const;
    CC_INLINE Real       getDeterminant();
    
    CC_INLINE Matrix3<value_type>&    identity();
    CC_INLINE Matrix3<value_type>&    transpose();                            // transpose itself and return reference
    CC_INLINE Matrix3<value_type>&    invert();
    
    // operators
    CC_INLINE Matrix3<value_type>     operator+(const Matrix3<value_type>& rhs) const;    // add rhs
    CC_INLINE Matrix3<value_type>     operator-(const Matrix3<value_type>& rhs) const;    // subtract rhs
    CC_INLINE Matrix3<value_type>&    operator+=(const Matrix3<value_type>& rhs);         // add rhs and update this object
    CC_INLINE Matrix3<value_type>&    operator-=(const Matrix3<value_type>& rhs);         // subtract rhs and update this object
    CC_INLINE Vector3<value_type>     operator*(const Vector3<value_type>& rhs) const;    // multiplication: v' = M * v
    CC_INLINE Matrix3<value_type>     operator*(const Matrix3<value_type>& rhs) const;    // multiplication: M3 = M1 * M2
    CC_INLINE Matrix3<value_type>&    operator*=(const Matrix3<value_type>& rhs);         // multiplication: M1' = M1 * M2
    CC_INLINE Boolean                 operator==(const Matrix3<value_type>& rhs) const;   // exact compare, no epsilon
    CC_INLINE Boolean                 operator!=(const Matrix3<value_type>& rhs) const;   // exact compare, no epsilon
    CC_INLINE value_type              operator[](Int32 index) const;            // subscript operator v[0], v[1]
    CC_INLINE value_type&             operator[](Int32 index);                  // subscript operator v[0], v[1]
    
    CC_INLINE Matrix3<value_type> operator-() const;                     // unary operator (-)
    CC_INLINE Matrix3<value_type> operator*(Real scalar) const;       // pre-multiplication
    
public:
    union {
        struct {
            value_type m00, m01, m02;
            value_type m10, m11, m12;
            value_type m20, m21, m22;
        };
        struct {
            value_type M00, M01, M02;
            value_type M10, M11, M12;
            value_type M20, M21, M22;
        };
        value_type m[9];
    };
    value_type tm[9];
};

#include "CCMatrix3.cpp"

typedef Matrix3<Int8> Matrix3b;
typedef Matrix3<Int16> Matrix3s;
typedef Matrix3<UInt16> Matrix3us;
typedef Matrix3<Int32> Matrix3i;
typedef Matrix3<UInt32> Matrix3ui;
typedef Matrix3<Int64> Matrix3l;
typedef Matrix3<UInt64> Matrix3ul;
typedef Matrix3<Real> Matrix3f;

///////////////////////////////////////////////////////////////////////////
// 4x4 matrix
///////////////////////////////////////////////////////////////////////////
template<typename T>
class Matrix4 {
public:
    typedef T value_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    
public:
    union {
        struct {
            value_type m00, m01, m02, m03;
            value_type m10, m11, m12, m13;
            value_type m20, m21, m22, m23;
            value_type m30, m31, m32, m33;
        };
        struct {
            value_type M00, M01, M02, M03;
            value_type M10, M11, M12, M13;
            value_type M20, M21, M22, M23;
            value_type M30, M31, M32, M33;
        };
        value_type m[16];
    };
private:
    Real getCofactor(value_type m0, value_type m1, value_type m2, value_type m3,
                     value_type m4, value_type m5, value_type m6, value_type m7,
                     value_type m8, value_type m9, value_type m10, value_type m11,
                     value_type m12, value_type m13, value_type m14, value_type m15);
    value_type tm[16];
    
public:
    explicit Matrix4();
    explicit Matrix4(value_type _00, value_type _01, value_type _02, value_type _03,
                     value_type _10, value_type _11, value_type _12, value_type _13,
                     value_type _20, value_type _21, value_type _22, value_type _23,
                     value_type _30, value_type _31, value_type _32, value_type _33);
    explicit Matrix4(const value_type m[16]);
    
    Matrix4(const Matrix4& otherM);
    
public:
    CC_INLINE void set(const value_type src[16]);
    CC_INLINE void set(value_type _00, value_type _01, value_type _02, value_type _03,
                       value_type _10, value_type _11, value_type _12, value_type _13,
                       value_type _20, value_type _21, value_type _22, value_type _23,
                       value_type _30, value_type _31, value_type _32, value_type _33);
    CC_INLINE void setRow(Int32 index, const value_type row[4]);
    CC_INLINE void setRow(Int32 index, const Vector3<value_type>& v);
    CC_INLINE void setRow(Int32 index, const Vector4<value_type>& v);
    
    CC_INLINE void setColumn(Int32 index, const value_type column[4]);
    CC_INLINE void setColumn(Int32 index, const Vector3<value_type>& other);
    CC_INLINE void setColumn(Int32 index, const Vector4<value_type>& other);
    
    CC_INLINE const value_type* get() const;
    CC_INLINE const value_type* getTranspose();
    CC_INLINE Real getDeterminant();
    
    CC_INLINE Matrix4<value_type>& identity();
    CC_INLINE Matrix4<value_type>& transpose();
    CC_INLINE Matrix4<value_type>& invert();
    CC_INLINE Matrix4<value_type>& invertEuclidean();
    CC_INLINE Matrix4<value_type>& invertAffine();
    CC_INLINE Matrix4<value_type>& invertProjective();
    CC_INLINE Matrix4<value_type>& invertGeneral();
    
    /// transform matrix
    CC_INLINE Matrix4<value_type>& translate(const value_type x, const value_type y, const value_type z);
    CC_INLINE Matrix4<value_type>& translate(const Vector3<value_type>& v);
    CC_INLINE Matrix4<value_type>& rotate(Real angle, const Vector3f& axis);
    CC_INLINE Matrix4<value_type>& rotate(Real angle, const Real x, const Real y, const Real z);
    CC_INLINE Matrix4<value_type>& rotateX(Real angle);
    CC_INLINE Matrix4<value_type>& rotateY(Real angle);
    CC_INLINE Matrix4<value_type>& rotateZ(Real angle);
    CC_INLINE Matrix4<value_type>& scale(const Real scale);
    CC_INLINE Matrix4<value_type>& scale(const Real sx, const Real sy, const Real sz);
    
    /// operators
    CC_INLINE Matrix4<value_type> operator+(const Matrix4<value_type>& other) const;
    CC_INLINE Matrix4<value_type> operator-(const Matrix4<value_type>& other) const;
    CC_INLINE Matrix4<value_type>& operator+=(const Matrix4<value_type>& other);
    CC_INLINE Matrix4<value_type>& operator-=(const Matrix4<value_type>& other);
    CC_INLINE Vector4<value_type> operator*(const Vector4<value_type>& other) const;
    CC_INLINE Vector3<value_type> operator*(const Vector3<value_type>& other) const;
    CC_INLINE Matrix4<value_type> operator*(const Matrix4<value_type>& other) const;
    CC_INLINE Matrix4<value_type>& operator*=(const Matrix4<value_type>& other);
    CC_INLINE Boolean operator==(const Matrix4& other) const;
    CC_INLINE Boolean operator!=(const Matrix4& other) const;
    CC_INLINE value_type operator[](const Int32 index) const;
    CC_INLINE value_type& operator[](const Int32 index);
    
    CC_INLINE Matrix4<value_type> operator-() const;
    CC_INLINE Matrix4<value_type> operator*(Real s) const;
    
};

#include "CCMatrix4.cpp"

typedef Matrix4<Int8> Matrix4b;
typedef Matrix4<Int16> Matrix4s;
typedef Matrix4<UInt16> Matrix4us;
typedef Matrix4<Int32> Matrix4i;
typedef Matrix4<UInt32> Matrix4ui;
typedef Matrix4<Int64> Matrix4l;
typedef Matrix4<UInt64> Matrix4ul;
typedef Matrix4<Real> Matrix4f;

#endif
